package com.android.accessibility.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;

import org.json.JSONObject;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.text.TextUtils;

import com.android.accessibility.service.MyAccessibilityService;

public class Utils {
    private static char HEX_DIGITS[] = { '0', '1', '2', '3', '4', '5', '6', '7','8', '9',
        'A', 'B', 'C', 'D', 'E', 'F' };

    public static String string2MD5(String inStr) {
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (Exception e) {
            Log.d(Log.TAG, "error : " + e);
            return "";
        }
        char[] charArray = inStr.toCharArray();
        byte[] byteArray = new byte[charArray.length];
        for (int i = 0; i < charArray.length; i++) {
            byteArray[i] = (byte) charArray[i];
        }
        byte[] md5Bytes = md5.digest(byteArray);
        StringBuffer hexValue = new StringBuffer();
        for (int i = 0; i < md5Bytes.length; i++) {
            int val = md5Bytes[i] & 0xff;
            if (val < 16) {
                hexValue.append("0");
            }
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();
    }

    private static String toHexString(byte[] b) {
        StringBuilder sb = new StringBuilder(b.length * 2);
        for (int i = 0; i < b.length; i++) {
            sb.append(HEX_DIGITS[(b[i] & 0xf0) >>> 4]);
            sb.append(HEX_DIGITS[b[i] & 0x0f]);
        }
        return sb.toString();
    }

    public static String md5sum(String filename) {
        InputStream fis;
        byte[] buffer = new byte[1024];
        int numRead = 0;
        MessageDigest md5 = null;
        try {
            fis = new FileInputStream(filename);
            md5 = MessageDigest.getInstance("MD5");
            while ((numRead = fis.read(buffer)) > 0) {
                md5.update(buffer, 0, numRead);
            }
            fis.close();
            return toHexString(md5.digest());
        } catch (Exception e) {
            Log.d(Log.TAG, "error : " + e);
        }
        return null;
    }

    public static String md5sumAssetsFile(Context context, String filename) {
        InputStream fis;
        byte[] buffer = new byte[1024];
        int numRead = 0;
        MessageDigest md5 = null;
        try {
            fis = context.getAssets().open(filename);
            md5 = MessageDigest.getInstance("MD5");
            while ((numRead = fis.read(buffer)) > 0) {
                md5.update(buffer, 0, numRead);
            }
            fis.close();
            return toHexString(md5.digest());
        } catch (Exception e) {
            Log.d(Log.TAG, "error : " + e);
        }
        return null;
    }

    public static String md5Bitmap(Bitmap bitmap) {
        MessageDigest md5 = null;
        try {
            byte []buffer = bitmapToByte(bitmap);
            md5 = MessageDigest.getInstance("MD5");
            md5.update(buffer, 0, buffer.length);
            return toHexString(md5.digest());
        } catch (Exception e) {
            Log.d(Log.TAG, "error : " + e);
        }
        return null;
    }

    public static byte[] bitmapToByte(Bitmap bitmap) {
        byte buffer[] = null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            bitmap.compress(CompressFormat.PNG, 90, baos);
            buffer = baos.toByteArray();
            baos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buffer;
    }

    public static InputStream bitmapToInputStream(Bitmap bitmap) {
        return new ByteArrayInputStream(bitmapToByte(bitmap));
    }

    public static String getString(Context context, String key) {
        return getString(context, key, "");
    }

    public static String getString(Context context, String key,
            String defaultValue) {
        try {
            return PreferenceManager.getDefaultSharedPreferences(context)
                    .getString(key, defaultValue);
        } catch (Exception e) {
            Log.d(Log.TAG, "error : " + e);
        }
        return defaultValue;
    }

    public static void putString(Context context, String key, String value) {
        PreferenceManager.getDefaultSharedPreferences(context).edit()
        .putString(key, value).commit();
    }

    public static int getInt(Context context, String key) {
        return getInt(context, key, -1);
    }

    public static int getInt(Context context, String key, int defaultValue) {
        try {
            return PreferenceManager.getDefaultSharedPreferences(context)
                    .getInt(key, defaultValue);
        } catch (Exception e) {
            Log.d(Log.TAG, "error : " + e);
        }
        return defaultValue;
    }

    public static void putInt(Context context, String key, int value) {
        PreferenceManager.getDefaultSharedPreferences(context).edit()
        .putInt(key, value).commit();
    }

    public static String getInstalledVersion(Context context, String packageName) {
        if (TextUtils.isEmpty(packageName)) {
            return null;
        }

        PackageInfo info = getPackageInfor(context, packageName);
        if (info != null) {
            return info.versionName;
        }

        return null;
    }

    public static PackageInfo getPackageInfor(Context context, String pkg) {
        PackageInfo infor = null;
        try {
            infor = context.getPackageManager().getPackageInfo(pkg, 0);
        } catch (NameNotFoundException e) {
            // Log.d(Log.TAG, "error : " + e);
        }
        return infor;
    }

    public static String getAppVersion(Context context) {
        try {
            PackageManager mPm = context.getPackageManager();
            PackageInfo info = mPm.getPackageInfo(context.getPackageName(), 0);
            return info.versionName;
        } catch (Exception e) {
            Log.d(Log.TAG, "error : " + e);
        }
        return null;
    }

    public static String getReleaseTime(Context context) {
        String releaseTime = null;
        try {
            ApplicationInfo appInfo = context.getPackageManager()
                    .getApplicationInfo(context.getPackageName(),
                            PackageManager.GET_META_DATA);
            if (appInfo != null) {
                releaseTime = appInfo.metaData.getString("releasetime");
            }
        } catch (Exception e) {
            Log.d(Log.TAG, "error : " + e);
        }
        return releaseTime;
    }

    public static String getChannelName(Context context) {
        String channelName = null;
        try {
            ApplicationInfo appInfo = context.getPackageManager()
                    .getApplicationInfo(context.getPackageName(),
                            PackageManager.GET_META_DATA);
            if (appInfo != null) {
                channelName = appInfo.metaData.getString("channelname");
            }
        } catch (Exception e) {
            Log.d(Log.TAG, "error : " + e);
        }
        return channelName;
    }

    public static String getStatType(Context context) {
        String releaseTime = null;
        try {
            ApplicationInfo appInfo = context.getPackageManager()
                    .getApplicationInfo(context.getPackageName(),
                            PackageManager.GET_META_DATA);
            if (appInfo != null) {
                releaseTime = appInfo.metaData.getString("stattype");
            }
        } catch (Exception e) {
            Log.d(Log.TAG, "error : " + e);
        }
        return releaseTime;
    }

    public static void showInstallActivity(Context context, String path) {
        PackageManager pm = context.getPackageManager();
        PackageInfo info = null;
        String realPath = null;
        try {
            Uri uri = Uri.parse(path);
            realPath = uri.getPath();
            info = pm.getPackageArchiveInfo(realPath,
                    PackageManager.GET_PERMISSIONS);
        } catch (Exception e) {
            Log.d(Log.TAG, "error : " + e);
        }
        if (info == null || TextUtils.isEmpty(realPath)) {
            // ToastUtils.showMessage(context, R.string.bad_apkfile);
            return;
        }
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
        intent.setDataAndType(Uri.fromFile(new File(realPath)), "application/vnd.android.package-archive");
        context.startActivity(intent);
    }

    public static boolean isInstalled(Context context, String packageName) {
        if (TextUtils.isEmpty(packageName)) {
            return false;
        }
        try {
            PackageInfo info = context.getPackageManager().getPackageInfo(
                    packageName, PackageManager.GET_PERMISSIONS);
            if (info != null) {
                return true;
            }
        } catch (NameNotFoundException e) {
            // Log.d(Log.TAG, "error : " + e);
        }
        return false;
    }

    public static int getVersionCode(Context context, String packageName) {
        if (context == null) {
            return -1;
        }
        if (TextUtils.isEmpty(packageName)) {
            packageName = context.getPackageName();
        }
        PackageInfo info = getPackageInfor(context, packageName);
        if (info != null) {
            return info.versionCode;
        }
        return -1;
    }

    public static void executeStartAppAction(Context context, String packageName) {
        Intent launchIntent = context.getPackageManager()
                .getLaunchIntentForPackage(packageName);
        launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(launchIntent);
    }

    public static String formatAppSize(long size, String prefix) {
        StringBuilder sb = new StringBuilder();
        if (!TextUtils.isEmpty(prefix)) {
            sb.append(prefix);
        }
        String str = null;
        DecimalFormat decimalFormat = new DecimalFormat("#.0");
        if (size > 1024 * 1024) {
            sb.append(decimalFormat.format((double) size / (1024 * 1024)))
            .append("M");
            str = sb.toString();
        } else {
            sb.append(size / 1024).append("K").toString();
            str = sb.toString();
        }
        return str;
    }

    public static void unInstallApk(Context context, String packName) {
        Uri uri = Uri.parse("package:" + packName);
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_DELETE);
        intent.setData(uri);
        context.startActivity(intent);
    }

    public static String getExceptionLogDir(Context context) {
        String path = null;
        try {
            if (Environment.getExternalStorageState().equals(
                    Environment.MEDIA_MOUNTED)) {
                path = Environment.getExternalStorageDirectory().toString()
                        + "/acc/log/";
            } else {
                path = context.getCacheDir().toString() + "/acc/log/";
            }
        } catch (Exception e) {
            Log.d(Log.TAG, "error : " + e);
        }
        return path;
    }

    public static String getPackageNameFromApkFile(Context context,
            String filePath) {
        PackageManager pm = context.getPackageManager();
        PackageInfo info = pm.getPackageArchiveInfo(filePath,
                PackageManager.GET_ACTIVITIES);
        if (info != null) {
            try {
                ApplicationInfo appInfo = info.applicationInfo;
                appInfo.sourceDir = filePath;
                appInfo.publicSourceDir = filePath;
                return appInfo.packageName;
            } catch (OutOfMemoryError e) {
            } catch (Exception e) {
            }
        }
        return null;
    }

    public static String getApkNameFromApkFile(Context context, String filePath) {
        PackageManager pm = context.getPackageManager();
        PackageInfo info = pm.getPackageArchiveInfo(filePath,
                PackageManager.GET_ACTIVITIES);
        if (info != null) {
            try {
                ApplicationInfo appInfo = info.applicationInfo;
                appInfo.sourceDir = filePath;
                appInfo.publicSourceDir = filePath;
                return appInfo.loadLabel(pm).toString();
            } catch (OutOfMemoryError e) {
            } catch (Exception e) {
            }
        }
        return null;
    }

    public static void putJsonDataSafed(JSONObject jo, String name, Object value) {
        try {
            if (!jo.has(name) && value != null) {
                jo.put(name, value);
            }
        } catch (Exception e) {
        }
    }

    public static String getJsonDataSafed(JSONObject jo, String name) {
        String jString = "";
        try {
            if (jo.has(name) && !jo.isNull(name)) {
                jString = jo.getString(name);
            }
        } catch (Exception e) {
        }

        return jString;
    }

    public static int parseInt(String str) {
        try  {
            return Integer.parseInt(str);
        } catch(Exception e) {
        }
        return 0;
    }

    public static String getDownloadType() {
        return "acc/downloads";
    }

    public static String getLogType() {
        return "acc/log";
    }

    public static File getDownloadDir(Context context) {
        String downloadDir = Utils.getDownloadType();
        File downDir = Environment.getExternalStoragePublicDirectory(downloadDir);
        if (downDir != null) {
            downDir.mkdirs();
            if (downDir.exists()) {
                return downDir;
            }
        }
        downDir = context.getExternalFilesDir(downloadDir);
        if (downDir != null) {
            downDir.mkdirs();
            if (downDir.exists()) {
                return downDir;
            }
        }
        return null;
    }

    public static File getHeaderPhotoFile(Context context) {
        return getUploadFile(context, "header_photo.png");
    }

    public static File getRealPhotoFile(Context context) {
        return getUploadFile(context, "real_photo.png");
    }

    private static File getUploadFile(Context context, String fileName) {
        // 保存裁剪后的图片文件
        File externalDir = Environment.getExternalStorageDirectory();
        if (externalDir == null && context != null) {
            externalDir = context.getCacheDir();
        }
        if (externalDir == null) {
            return null;
        }
        File pictureFileDir = new File(
                externalDir, "/upload");
        if (!pictureFileDir.exists()) {
            pictureFileDir.mkdirs();
        }
        File picFile = new File(pictureFileDir, fileName);
        if (picFile.exists()) {
            picFile.delete();
        }
        try {
            picFile.createNewFile();
        } catch (IOException e) {
            return null;
        }
        return picFile;
    }

    public static int dp2px(Context context, float dp) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }

    public static int px2dp(Context context, float px) {
        final float scale = context.getResources().getDisplayMetrics().density;
        Log.d(Log.TAG, "scale = " + scale);
        return (int) (px / scale + 0.5f);
    }

    public static boolean copyFile(String oldPath, String newPath) {
        try {
            int byteread = 0;
            File oldfile = new File(oldPath);
            if (oldfile.exists()) { // 文件存在时
                InputStream inStream = new FileInputStream(oldPath); // 读入原文件
                FileOutputStream fs = new FileOutputStream(newPath);
                byte[] buffer = new byte[4096];
                while ((byteread = inStream.read(buffer)) != -1) {
                    fs.write(buffer, 0, byteread);
                }
                inStream.close();
                fs.close();
                return true;
            }
        } catch (Exception e) {
            Log.d(Log.TAG, "error : " + e);
        }
        return false;
    }

    /**
     * 判断某个服务是否正在运行的方法
     * @param context
     * @param serviceName
     *            是包名+服务的类名（例如：net.loonggg.testbackstage.TestService）
     * @return true代表正在运行，false代表服务没有正在运行
     */
    public static boolean isServiceWork(Context context, String serviceName) {
        boolean isWork = false;
        ActivityManager myAM = (ActivityManager) context
                .getSystemService(Context.ACTIVITY_SERVICE);
        List<RunningServiceInfo> myList = myAM.getRunningServices(40);
        if (myList.size() <= 0) {
            return false;
        }
        for (int i = 0; i < myList.size(); i++) {
            String mName = myList.get(i).service.getClassName().toString();
            if (mName.equals(serviceName)) {
                isWork = true;
                break;
            }
        }
        return isWork;
    }

    // To check if service is enabled
    public static boolean isAccessibilitySettingsOn(Context context) {
        int accessibilityEnabled = 0;
        final String service = context.getPackageName() + "/"
                + MyAccessibilityService.class.getCanonicalName();
        try {
            accessibilityEnabled = Settings.Secure.getInt(context
                    .getApplicationContext().getContentResolver(),
                    android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);
        } catch (Settings.SettingNotFoundException e) {
            Log.e(Log.TAG,
                    "Error finding setting, default accessibility to not found: "
                            + e.getMessage());
        }
        TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(
                ':');

        if (accessibilityEnabled == 1) {
            String settingValue = Settings.Secure.getString(context
                    .getApplicationContext().getContentResolver(),
                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
            if (settingValue != null) {
                mStringColonSplitter.setString(settingValue);
                while (mStringColonSplitter.hasNext()) {
                    String accessibilityService = mStringColonSplitter.next();
                    if (accessibilityService.equalsIgnoreCase(service)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public static String getTopPackage(Context context) {
        String pagekageName = null;
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        if (Build.VERSION.SDK_INT > 20) {
            try {
                pagekageName = am.getRunningAppProcesses().get(0).processName;
            } catch(Exception e) {
                Log.d(Log.TAG, "error : " + e);
            }
        } else {
            try {
                pagekageName = am.getRunningTasks(1).get(0).topActivity.getPackageName();
            } catch(Exception e) {
                Log.d(Log.TAG, "error : " + e);
            }
        }
        return pagekageName;
    }

    public static void writeFile(String content) {
        if (TextUtils.isEmpty(content)) {
            return;
        }
        try {
            FileOutputStream fos = new FileOutputStream(
                    Environment.getExternalStorageDirectory()
                    + "/aaaaaaaaaa.txt");
            fos.write(content.getBytes());
            fos.close();
        } catch (Exception e) {
        }
    }

    public static boolean forceAllowForward() {
        try {
            File external = Environment.getExternalStorageDirectory();
            File forceallow = new File(external + File.separator + "force.debug");
            if (forceallow.exists()) {
                return true;
            }
        } catch(Exception e) {
        }
        return false;
    }

    public static File getLogDir(Context context) {
        File logDir = Environment.getExternalStoragePublicDirectory("accessibility/log");
        if (logDir != null) {
            logDir.mkdirs();
            if (logDir.exists()) {
                return logDir;
            }
        }
        logDir = context.getExternalFilesDir("accessibility/log");
        if (logDir != null) {
            logDir.mkdirs();
            if (logDir.exists()) {
                return logDir;
            }
        }
        return null;
    }

    public static List<String> getRecursiveFolder(String rootPath) {
        File rootDir = new File(rootPath);
        if (!rootDir.exists()) {
            return null;
        }
        List<String> pathList = new ArrayList<String>();
        Stack<String> stack = new Stack<String>();
        stack.push(rootPath);

        while (!stack.isEmpty()) {
            String parent = (String) stack.pop();
            pathList.add(parent);
            File path = new File(parent);
            File[] files = path.listFiles();
            if (null == files)
                continue;
            for (File f : files) {
                if (f.isDirectory() && !f.getName().equals(".")
                        && !f.getName().equals("..")) {
                    stack.push(f.getPath());
                }
            }
        }
        return pathList;
    }

    public static String getTopActivity(Context context) {
        if (context == null) {
            return null;
        }
        ActivityManager am = (ActivityManager) context
                .getSystemService(Activity.ACTIVITY_SERVICE);
        if (am == null) {
            return null;
        }
        if (Build.VERSION.SDK_INT <= 20) {
            List<RunningTaskInfo> tasks = am.getRunningTasks(1);
            if (tasks != null && !tasks.isEmpty()) {
                ComponentName componentName = tasks.get(0).topActivity;
                if (componentName != null) {
                    return componentName.getClassName();
                }
            }
        } else {
            RunningAppProcessInfo currentInfo = null;
            Field field = null;
            int START_TASK_TO_FRONT = 2;
            String pkgName = null;
            try {
                field = RunningAppProcessInfo.class
                        .getDeclaredField("processState");
            } catch (Exception e) {
                return null;
            }
            List<RunningAppProcessInfo> appList = am.getRunningAppProcesses();
            if (appList == null || appList.isEmpty()) {
                return null;
            }
            for (RunningAppProcessInfo app : appList) {
                if (app != null
                        && app.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                    Integer state = null;
                    try {
                        state = field.getInt(app);
                    } catch (Exception e) {
                        return null;
                    }
                    if (state != null && state == START_TASK_TO_FRONT) {
                        currentInfo = app;
                        break;
                    }
                }
            }
            if (currentInfo != null) {
                pkgName = currentInfo.processName;
            }
            return pkgName;
        }
        return null;
    }

    public static void setAccessibility(Context context, int value) {
        PreferenceManager.getDefaultSharedPreferences(context).edit()
        .putInt("accessibility", value).commit();
    }

    public static int getAccessibility(Context context) {
        return PreferenceManager.getDefaultSharedPreferences(context).getInt(
                "accessibility", 0);
    }

    public static int getStatusBarHeight(Context context) {
        int result = 0;
        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = context.getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }

    public static void saveVideoDir(Context context, List<String> videoDir) {
        if (videoDir != null && videoDir.size() > 0) {
            Set<String> set = new HashSet<String>(videoDir);
            PreferenceManager.getDefaultSharedPreferences(context).edit().putStringSet("video_dir", set).commit();
        }
    }

    public static List<String> readVideoDir(Context context) {
        Set<String> set = PreferenceManager.getDefaultSharedPreferences(context).getStringSet("video_dir", null);
        if (set != null) {
            List<String> list = new ArrayList<String>(set);
            return list;
        }
        return null;
    }

    public static <T> void addAll(List<T> list, List<T> other) {
        if (list == null || other == null) {
            return;
        }
        for (T t : other) {
            list.add(t);
        }
    }

    public static List<String> findGivenameFolder(File root, String name) {
        List<String> fileList = new ArrayList<String>();
        findFoderRecurives(root, name, fileList);
        return fileList;
    }

    public static void findFoderRecurives(File root, String name, List<String> fileList) {
        if (root == null || TextUtils.isEmpty(name) || fileList == null) {
            return;
        }
        File files[] = root.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                if (name.equalsIgnoreCase(file.getName())) {
                    fileList.add(file.getAbsolutePath());
                } else {
                    findFoderRecurives(file, name, fileList);
                }
            }
        }
    }

    public static String getFullUrl(Context context, String fileName) {
        String channel = Utils.getChannelName(context);
        return Constant.URL_CONFIG + channel + "/" + fileName;
    }
}
